package com.dragons.dragonsvf.utils.secret;

import javax.crypto.*;
import javax.crypto.spec.*;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Arrays;
import java.util.Base64;

/**
 * @author Dragons.G
 * @date 2025-05-05 18:02:48
 * @description DES 安全加密算法
 */
public class SecureEncryptionUtil {

    // 测试用例
    public static void main(String[] args) {
        String original = "dragons1314127";
        String password = "DRAGONS.G"; // 自动过滤非法字符

        String encrypted = encrypt(original, password);
        System.out.println("加密结果: " + encrypted);

        String decrypted = decrypt(encrypted, password);
        System.out.println("解密结果: " + decrypted);
        System.out.println("验证结果: " + original.equals(decrypted));
    }

    private static final String ALLOWED_CHAR_SET = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789"; // 过滤易混淆字符
    private static final int SALT_LENGTH = 16;
    private static final int IV_LENGTH = 16;
    private static final int ITERATIONS = 65536;
    private static final int KEY_SIZE = 128;

    // 密码预处理（过滤非法字符）
    public static String sanitizePassword(String password) {
        return password.chars()
            .mapToObj(c -> String.valueOf((char) c))
            .filter(c -> ALLOWED_CHAR_SET.contains(c))
            .reduce("", String::concat);
    }

    // 生成PBKDF2密钥
    private static SecretKey generateKey(String password, byte[] salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
        SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        KeySpec spec = new PBEKeySpec(password.toCharArray(), salt, ITERATIONS, KEY_SIZE);
        SecretKey tmp = factory.generateSecret(spec);
        return new SecretKeySpec(tmp.getEncoded(), "AES");
    }

    // AES加密（CBC模式）
    public static String encrypt(String plainText, String password) {
        try {
            byte[] salt = generateRandomBytes(SALT_LENGTH);
            SecretKey secretKey = generateKey(sanitizePassword(password), salt);

            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            byte[] iv = generateRandomBytes(IV_LENGTH);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, new IvParameterSpec(iv));

            byte[] encrypted = cipher.doFinal(plainText.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(
                ByteBuffer.allocate(1 + SALT_LENGTH + IV_LENGTH + encrypted.length)
                    .put((byte) salt.length)
                    .put(salt)
                    .put(iv)
                    .put(encrypted)
                    .array()
            );
        } catch (Exception e) {
            throw new RuntimeException("加密失败", e);
        }
    }

    // AES解密
    public static String decrypt(String encryptedText, String password) {
        try {
            byte[] data = Base64.getDecoder().decode(encryptedText);
            int saltLength = data[0] & 0xFF;
            byte[] salt = Arrays.copyOfRange(data, 1, 1 + saltLength);
            byte[] iv = Arrays.copyOfRange(data, 1 + saltLength, 1 + saltLength + IV_LENGTH);
            byte[] cipherText = Arrays.copyOfRange(data, 1 + saltLength + IV_LENGTH, data.length);

            SecretKey secretKey = generateKey(sanitizePassword(password), salt);
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(iv));

            return new String(cipher.doFinal(cipherText), StandardCharsets.UTF_8);
        } catch (Exception e) {
            throw new RuntimeException("解密失败", e);
        }
    }

    // 生成随机字节数组
    private static byte[] generateRandomBytes(int length) {
        byte[] bytes = new byte[length];
        SecureRandom random = new SecureRandom();
        random.nextBytes(bytes);
        return bytes;
    }

    // 辅助方法：十六进制编码
    public static String bytesToHex(byte[] bytes) {
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02x", b));
        }
        return sb.toString();
    }

}
